﻿using System;
using System.Collections.Generic;
using System.Data;
using DataCenter.Share;
using Microsoft.SqlServer.Server;
using System.Reflection;

namespace DataCenter.Clr
{
    public abstract class Aggregation
    {
        public Aggregation(string styleStr)
        {
            this.StyleArray = styleStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        }
        protected string[] StyleArray;

        protected List<AggregateStyle> StyleList;        
        public virtual void Build(string styleStr)
        {
            StyleList = CreateStyleList();
        }
        public abstract void AddRecord(BinaryRecord record, int step, int[] operTypes);
        public abstract SqlDataRecord CreateDataRecord();
        public abstract void FillDataRecord(SqlDataRecord record);
        public static Aggregation CreateInstance(string groupStr, string styleStr, string parameters)
        {
            Aggregation aggregation = null;
            switch (groupStr.ToUpper())
            {
                case "": aggregation = new AggregationByNone(styleStr); break;
                case "GROUPBYID": aggregation = new AggregationById(styleStr); break;
                case "GROUPBYTIME": aggregation = new AggregationByTime(styleStr); break;
                case "GROUPBYINTERVAL": aggregation = new AggregationByInterval(styleStr); break;
                case "GROUPBYIDANDINTERVAL": aggregation = new AggregationByIdAndInterval(styleStr); break;
                default:
                    throw new InvalidOperationException("无效的分组方式\"" + groupStr + "\"");
            }
            if (aggregation != null)
            {
                if (!string.IsNullOrEmpty(parameters))
                {
                    Type theType = aggregation.GetType();

                    string[] paramArray = parameters.Split(new char[] { ',', '=' }, StringSplitOptions.RemoveEmptyEntries);
                    for (int i = 0; i < paramArray.Length; i += 2)
                    {
                        FieldInfo fieldInfo = theType.GetField(paramArray[i], 
                            BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic);
                        if (fieldInfo == null)
                            throw new InvalidOperationException("找不到参数\"" + paramArray[i] + "\"");
                        object oValue = Convert.ChangeType(paramArray[i + 1], fieldInfo.FieldType);
                        fieldInfo.SetValue(aggregation, oValue);
                    }
                }
            }
            return aggregation;
        }
        protected List<AggregateStyle> CreateStyleList()
        {
            List<AggregateStyle> styleList = new List<AggregateStyle>();
            for (int i = 0; i < StyleArray.Length; i += 2)
            {
                AggregateStyle style = AggregateStyle.CreateInstance(StyleArray[i], StyleArray[i + 1]);
                styleList.Add(style);
            }
            return styleList;
        }
    }

    public class AggregationByNone : Aggregation
    {
        public AggregationByNone(string styleStr)
            : base(styleStr) { }

        public override void AddRecord(BinaryRecord record, int step, int[] operTypes)
        {
            IIterator iterator = (IIterator)record.BinaryValue;
            iterator.Step = step;
            iterator.OperTypes = operTypes;
            iterator.Reset();
            while (iterator.MoveNext())
            {
                ResultRecordRow row = (ResultRecordRow)iterator.Current;
                row.ObjectId = record.ObjectId;
                row.ValueType = record.ObjectId;
                foreach (AggregateStyle style in StyleList)
                {
                    style.Aggregate(row);
                }
            }
        }

        public override SqlDataRecord CreateDataRecord()
        {
            SqlMetaData[] metaDatas = new SqlMetaData[StyleList.Count];
            for (int i = 0; i < StyleList.Count; i++)
                metaDatas[i] = new SqlMetaData("R__" + i, SqlDbType.Real);
            SqlDataRecord record = new SqlDataRecord(metaDatas);
            return record;
        }

        public override void FillDataRecord(SqlDataRecord record)
        {
            for (int i = 0; i < StyleList.Count; i++)
            {
                float value = StyleList[i].GetValue();
                record.SetFloat(i, value);
            }
            SqlContext.Pipe.SendResultsRow(record);
        }
    }

    public class AggregationById : Aggregation
    {
        public AggregationById(string styleStr)
            :base(styleStr)
        {
            this.DictStyle = new Dictionary<int, List<AggregateStyle>>();
        }
        private Dictionary<int, List<AggregateStyle>> DictStyle;

        public override void AddRecord(BinaryRecord record, int step, int[] operTypes)
        {
            List<AggregateStyle> styleList = null;
            if (DictStyle.ContainsKey(record.ObjectId))
                styleList = DictStyle[record.ObjectId];
            else
            {
                styleList = CreateStyleList();
                DictStyle.Add(record.ObjectId, styleList);
            }
            IIterator iterator = (IIterator)record.BinaryValue;
            iterator.Step = step;
            iterator.OperTypes = operTypes;
            iterator.Reset();
            while (iterator.MoveNext())
            {
                ResultRecordRow row = (ResultRecordRow)iterator.Current;
                row.ObjectId = record.ObjectId;
                row.ValueType = record.ObjectId;
                foreach (AggregateStyle style in styleList)
                {
                    style.Aggregate(row);
                }
            }
        }

        public override SqlDataRecord CreateDataRecord()
        {
            SqlMetaData[] metaDatas = new SqlMetaData[StyleList.Count + 1];
            metaDatas[0] = new SqlMetaData("ObjectId", SqlDbType.Int);
            for (int i = 0; i < StyleList.Count; i++)
                metaDatas[i + 1] = new SqlMetaData("R__" + i, SqlDbType.Real);
            SqlDataRecord record = new SqlDataRecord(metaDatas);
            return record;
        }

        public override void FillDataRecord(SqlDataRecord record)
        {
            foreach (int objectId in DictStyle.Keys)
            {
                List<AggregateStyle> styleList = DictStyle[objectId];
                record.SetInt32(0, objectId);
                for (int i = 0; i < styleList.Count; i++)
                {
                    float value = styleList[i].GetValue();
                    record.SetFloat(i + 1, value);
                }
                SqlContext.Pipe.SendResultsRow(record);
            }            
        }
    }

    public class AggregationByTime : Aggregation
    {
        public AggregationByTime(string styleStr)
            : base(styleStr)
        {
            this.DictStyle = new SortedList<DateTime, List<AggregateStyle>>();
        }
        private SortedList<DateTime, List<AggregateStyle>> DictStyle;

        public override void AddRecord(BinaryRecord record, int step, int[] operTypes)
        {
            IIterator iterator = (IIterator)record.BinaryValue;
            iterator.Step = step;
            iterator.OperTypes = operTypes;
            iterator.Reset();
            while (iterator.MoveNext())
            {
                ResultRecordRow row = (ResultRecordRow)iterator.Current;
                row.ObjectId = record.ObjectId;
                row.ValueType = record.ObjectId;

                List<AggregateStyle> styleList = null;
                if (DictStyle.ContainsKey(row.ValueTime))
                    styleList = DictStyle[row.ValueTime];
                else
                {
                    styleList = CreateStyleList();
                    DictStyle.Add(row.ValueTime, styleList);
                }
                foreach (AggregateStyle style in styleList)
                {
                    style.Aggregate(row);
                }
            }
        }

        public override SqlDataRecord CreateDataRecord()
        {
            SqlMetaData[] metaDatas = new SqlMetaData[StyleList.Count + 1];
            metaDatas[0] = new SqlMetaData("ValueTime", SqlDbType.DateTime);
            for (int i = 0; i < StyleList.Count; i++)
                metaDatas[i + 1] = new SqlMetaData("R__" + i, SqlDbType.Real);
            SqlDataRecord record = new SqlDataRecord(metaDatas);
            return record;
        }

        public override void FillDataRecord(SqlDataRecord record)
        {
            foreach (DateTime valueTime in DictStyle.Keys)
            {                
                record.SetDateTime(0, valueTime);
                List<AggregateStyle> styleList = DictStyle[valueTime];
                for (int i = 0; i < styleList.Count; i++)
                {
                    float value = styleList[i].GetValue();
                    record.SetFloat(i + 1, value);
                }
                SqlContext.Pipe.SendResultsRow(record);
            }
        }
    }

    public class AggregationByInterval : Aggregation
    {
        public AggregationByInterval(string styleStr)
            : base(styleStr)
        {
            this.DictStyle = new SortedList<double, List<AggregateStyle>>();
        }
        private int Interval = 1;
        private SortedList<double, List<AggregateStyle>> DictStyle;

        public override void AddRecord(BinaryRecord record, int step, int[] operTypes)
        {
            IIterator iterator = (IIterator)record.BinaryValue;
            iterator.Step = step;
            iterator.OperTypes = operTypes;
            iterator.Reset();
            while (iterator.MoveNext())
            {
                ResultRecordRow row = (ResultRecordRow)iterator.Current;
                row.ObjectId = record.ObjectId;
                row.ValueType = record.ObjectId;

                double minutes = Math.Floor(GetTotalMinutes(row.ValueTime) / Interval);
                List<AggregateStyle> styleList = null;
                if (DictStyle.ContainsKey(minutes))
                    styleList = DictStyle[minutes];
                else
                {
                    styleList = CreateStyleList();
                    DictStyle.Add(minutes, styleList);
                }
                foreach (AggregateStyle style in styleList)
                {
                    style.Aggregate(row);
                }
            }
        }

        public override SqlDataRecord CreateDataRecord()
        {
            SqlMetaData[] metaDatas = new SqlMetaData[StyleList.Count + 1];
            metaDatas[0] = new SqlMetaData("ValueTime", SqlDbType.DateTime);
            for (int i = 0; i < StyleList.Count; i++)
                metaDatas[i + 1] = new SqlMetaData("R__" + i, SqlDbType.Real);
            SqlDataRecord record = new SqlDataRecord(metaDatas);
            return record;
        }

        public override void FillDataRecord(SqlDataRecord record)
        {
            foreach (double minutes in DictStyle.Keys)
            {
                DateTime valueTime = Utils.EMPTY_TIME.AddMinutes(minutes * Interval);
                record.SetDateTime(0, valueTime);
                List<AggregateStyle> styleList = DictStyle[minutes];
                for (int i = 0; i < styleList.Count; i++)
                {
                    float value = styleList[i].GetValue();
                    record.SetFloat(i + 1, value);
                }
                SqlContext.Pipe.SendResultsRow(record);
            }
        }

        private double GetTotalMinutes(DateTime valueTime)
        {
            return (valueTime - Utils.EMPTY_TIME).TotalMinutes;
        }
    }

    public class AggregationByIdAndInterval : Aggregation
    {
        public AggregationByIdAndInterval(string styleStr)
            : base(styleStr)
        {
            this.DictStyle = new Dictionary<int, SortedList<double, List<AggregateStyle>>>();
        }
        private int Interval = 1;
        private Dictionary<int, SortedList<double, List<AggregateStyle>>> DictStyle;

        public override void AddRecord(BinaryRecord record, int step, int[] operTypes)
        {
            SortedList<double, List<AggregateStyle>> dictInterval = null;
            if (DictStyle.ContainsKey(record.ObjectId))
                dictInterval = DictStyle[record.ObjectId];
            else
            {
                dictInterval = new SortedList<double, List<AggregateStyle>>();
                DictStyle.Add(record.ObjectId, dictInterval);
            }

            IIterator iterator = (IIterator)record.BinaryValue;
            iterator.Step = step;
            iterator.OperTypes = operTypes;
            iterator.Reset();
            while (iterator.MoveNext())
            {
                ResultRecordRow row = (ResultRecordRow)iterator.Current;
                row.ObjectId = record.ObjectId;
                row.ValueType = record.ObjectId;

                double minutes = Math.Floor(GetTotalMinutes(row.ValueTime) / Interval);
                List<AggregateStyle> styleList = null;
                if (dictInterval.ContainsKey(minutes))
                    styleList = dictInterval[minutes];
                else
                {
                    styleList = CreateStyleList();
                    dictInterval.Add(minutes, styleList);
                }
                foreach (AggregateStyle style in styleList)
                {
                    style.Aggregate(row);
                }
            }
        }

        public override SqlDataRecord CreateDataRecord()
        {
            SqlMetaData[] metaDatas = new SqlMetaData[StyleList.Count + 2];
            metaDatas[0] = new SqlMetaData("ObjectId", SqlDbType.Int);
            metaDatas[1] = new SqlMetaData("ValueTime", SqlDbType.DateTime);
            for (int i = 0; i < StyleList.Count; i++)
                metaDatas[i + 2] = new SqlMetaData("R__" + i, SqlDbType.Real);
            SqlDataRecord record = new SqlDataRecord(metaDatas);
            return record;
        }

        public override void FillDataRecord(SqlDataRecord record)
        {
            foreach (int objectId in DictStyle.Keys)
            {
                record.SetInt32(0, objectId);
                SortedList<double, List<AggregateStyle>> dictInterval = DictStyle[objectId];
                foreach (double minutes in dictInterval.Keys)
                {
                    DateTime valueTime = Utils.EMPTY_TIME.AddMinutes(minutes * Interval);
                    record.SetDateTime(1, valueTime);
                    List<AggregateStyle> styleList = dictInterval[minutes];
                    for (int i = 0; i < styleList.Count; i++)
                    {
                        float value = styleList[i].GetValue();
                        record.SetFloat(i + 2, value);
                    }
                    SqlContext.Pipe.SendResultsRow(record);
                }
            }
        }

        private double GetTotalMinutes(DateTime valueTime)
        {
            return (valueTime - Utils.EMPTY_TIME).TotalMinutes;
        }
    }
}
